package tool.zyk.utils;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * author: zyk
 * datetime: 2024/2/3 21:49
 * describe:反射工具
 */
@Slf4j
public class ReflectUtils {
    private static final String paramSpector = "`";

    private final static Map<String, Class<?>> classInfoCache = new ConcurrentHashMap<>();

    private final static Map<String, Method> methodInfoCache = new ConcurrentHashMap<>();

    @SneakyThrows
    public static Object getObjectByClassname(String className) {
        return getClassType(className).newInstance();
    }
    public static Method getMethod(String classname, String methodCode) {
        String cacheKey = buildMethodCacheKey(classname, methodCode);
        Method method = methodInfoCache.get(cacheKey);
        if (Objects.nonNull(method)) return method;
        Method newMethod = parseGetMethod(classname, methodCode);
        methodInfoCache.put(cacheKey, newMethod);
        return newMethod;
    }

    private static String buildMethodCacheKey(String classname, String methodCode) {
        return classname + "%" + methodCode;
    }

    private static Method parseGetMethod(String classname, String methodCode) {
        Class<?> classType = getClassType(classname);
        return Arrays.stream(classType.getDeclaredMethods())
                .filter(method -> Objects.equals(encodeMethodCode(method), methodCode))
                .findFirst()
                .orElseThrow(() -> new RuntimeException(String.format("未知的方法编码,类名 %s,方法code %s", classname, methodCode)));

    }


    public static String encodeMethodCode(Method method) {
        Asserts.isNotNull(method, "入参方法不能为空");
        String methodName = method.getName();
        return methodName + paramSpector + Arrays.stream(method.getParameterTypes())
                .map(a -> Class.class.getName())
                .collect(Collectors.joining(paramSpector));
    }


    @SneakyThrows
    public static Class<?> getClassType(String classPath) {
        Class<?> cacheClass = classInfoCache.get(classPath);
        if (Objects.isNull(cacheClass)) {
            classInfoCache.put(classPath, Class.forName(classPath));
        }
        return classInfoCache.get(classPath);

    }
}
